using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using Widgetsphere.Core.EventArgs;

namespace Widgetsphere.Core.DataAccess
{
	public partial class BusinessObjectList<T> : IList<T>, IList, ITyped
	{
		#region Class Members

		public List<T> mBusinessObject;

		#endregion

		#region Constructor

		public BusinessObjectList()
		{
			mBusinessObject = new List<T>();
		}

		public BusinessObjectList(IEnumerable<T> collection)
		{
			mBusinessObject = new List<T>(collection);
		}

		public BusinessObjectList(int capacity)
		{
			mBusinessObject = new List<T>(capacity);
		}

		#endregion

		#region Events

		public event BusinessObjectEventHandler ObjectAdded;
		public event BusinessObjectEventHandler ObjectRemoved;

		protected virtual void OnObjectAdded(BusinessObjectEventArgs e)
		{
			if (this.ObjectAdded != null)
				this.ObjectAdded(this, e);
		}

		protected virtual void OnObjectRemoved(BusinessObjectEventArgs e)
		{
			if (this.ObjectRemoved != null)
				this.ObjectRemoved(this, e);
		}

		#endregion

		#region Expose List Methods
		public void Sort()
		{
			mBusinessObject.Sort();
		}
		public void Sort(Comparison<T> comparison)
		{
			mBusinessObject.Sort(comparison);
		}
		public void Sort(IComparer<T> comparer)
		{
			mBusinessObject.Sort(comparer);
		}
		#endregion

		#region IList Members

		public T this[int index]
		{
			get
			{
				return mBusinessObject[index];
			}
			set
			{
				this[index] = value;
			}
		}

		public void RemoveAt(int index)
		{
			if (ObjectRemoved != null)
			{
				this.OnObjectRemoved(new BusinessObjectEventArgs((IBusinessObject)mBusinessObject[index]));
			}
			mBusinessObject.RemoveAt(index);
		}

		public void Insert(int index, T element)
		{
			this.Insert(index, (IBusinessObject)element);
		}

		public void Insert(int index, IBusinessObject element)
		{
			if (ObjectRemoved != null)
			{
				this.OnObjectRemoved(new BusinessObjectEventArgs((IBusinessObject)mBusinessObject[index]));
			}
			mBusinessObject.Insert(index, (T)element);
			if (ObjectAdded != null)
			{
				this.OnObjectAdded(new BusinessObjectEventArgs((IBusinessObject)mBusinessObject[index]));
			}
		}

		public bool Remove(T element)
		{
			var count = this.Count;
			this.Remove((IBusinessObject)element);
			return this.Count < count;
		}

		public void Remove(IBusinessObject element)
		{
			var indexOf = this.IndexOf(element);
			if (indexOf >= 0)
			{
				this.RemoveAt(indexOf);
			}
		}

		public bool Contains(T element)
		{
			return this.Contains((IBusinessObject)element);
		}

		public bool Contains(IBusinessObject element)
		{
			var row1 = ((DataRow)element.WrappedClass);
			var keys1 = row1.Table.PrimaryKey;
			foreach (IBusinessObject loop in this)
			{
				var row2 = ((DataRow)loop.WrappedClass);
				var keys2 = row2.Table.PrimaryKey;

				//Verify key fields are the same
				if (keys1.Length != keys2.Length)
					throw new Exception("Element types do not match!");
				for (var ii = 0; ii < keys1.Length; ii++)
					if ((keys1[ii].ColumnName != keys2[ii].ColumnName) || (keys1[ii].DataType != keys2[ii].DataType))
						throw new Exception("Element types do not match!");

				//Check the value
				foreach (var column in keys1)
				{
					var bValue = false;
					//if (column.DataType == typeof(Guid))
					//  bValue = (row1[column.ColumnName].ToString() == row2[column.ColumnName].ToString());
					//else
					//  bValue = (row1[column.ColumnName] == row2[column.ColumnName]);
					bValue = (row1[column.ColumnName].ToString() == row2[column.ColumnName].ToString());
					if (bValue)
						return true;
				}
			}
			return false;
		}

		public void Clear()
		{
			for (var ii = mBusinessObject.Count - 1; ii >= 0; ii--)
			{
				this.RemoveAt(ii);
			}
			mBusinessObject.Clear();
		}

		public int IndexOf(T element)
		{
			return this.IndexOf((IBusinessObject)element);
		}

		public int IndexOf(IBusinessObject element)
		{
			var row1 = ((DataRow)element.WrappedClass);
			var keys1 = row1.Table.PrimaryKey;
			var index = 0;
			foreach (IBusinessObject loop in this)
			{
				var row2 = ((DataRow)loop.WrappedClass);
				var keys2 = row2.Table.PrimaryKey;

				//Verify key fields are the same
				if (keys1.Length != keys2.Length)
					throw new Exception("Element types do not match!");
				for (var ii = 0; ii < keys1.Length; ii++)
					if ((keys1[ii].ColumnName != keys2[ii].ColumnName) || (keys1[ii].DataType != keys2[ii].DataType))
						throw new Exception("Element types do not match!");

				//Check the value
				foreach (var column in keys1)
				{
					var bValue = false;
					bValue = (row1[column.ColumnName].ToString() == row2[column.ColumnName].ToString());
					if (bValue)
						return index;
				}

				index++;
			}
			return -1;
		}

		public void Add(T element)
		{
			this.Add((IBusinessObject)element);
		}

		public void Add(IBusinessObject element)
		{
			try
			{
				if (ObjectAdded != null)
				{
					this.OnObjectAdded(new BusinessObjectEventArgs(element));
				}
			}
			catch (InvalidConstraintException ex)
			{
				throw new Widgetsphere.Core.Exceptions.WSException("Framework usage error: The parent item must be added to its collection before creating the relationship.", ex);
			}
			mBusinessObject.Add((T)element);

		}

		public void AddRange(ICollection<T> c)
		{
			foreach (IBusinessObject o in c)
				this.Add(o);
		}

		#endregion

		#region ICollection Members

		public int Count
		{
			get { return mBusinessObject.Count; }
		}

		public void CopyTo(T[] array, int index)
		{
			mBusinessObject.CopyTo(array, index);
		}

		public void CopyTo(Array array, int index)
		{
			((IList)mBusinessObject).CopyTo(array, index);
		}

		public bool IsReadOnly
		{
			get { return false; }
		}

		public bool IsSynchronized
		{
			get
			{
				return ((IList)mBusinessObject).IsSynchronized;
			}
		}

		public object SyncRoot
		{
			get
			{
				return ((IList)mBusinessObject).SyncRoot;
			}
		}

		#endregion

		#region IEnumerable Members
		IEnumerator<T> IEnumerable<T>.GetEnumerator()
		{
			return ((IEnumerable<T>)mBusinessObject).GetEnumerator();
		}

		public System.Collections.IEnumerator GetEnumerator()
		{
			return mBusinessObject.GetEnumerator();
		}

		#endregion

		#region ITyped Members

		Type ITyped.GetContainedType()
		{
			return typeof(T);
		}

		#endregion

		#region Collection Event Handlers
		private void CollectionObjectAdded(object sender, IBusinessObject row)
		{
			this.Add(row);
		}

		private void CollectionObjectRemoved(object sender, IBusinessObject row)
		{
			this.Remove(row);
		}
		#endregion

		#region IList Members

		public int Add(object value)
		{
			return ((IList)mBusinessObject).Add(value);
		}

		public bool Contains(object value)
		{
			return ((IList)mBusinessObject).Contains(value);
		}

		public int IndexOf(object value)
		{
			return ((IList)mBusinessObject).IndexOf(value);
		}

		public void Insert(int index, object value)
		{
			((IList)mBusinessObject).Insert(index, value);
		}

		public bool IsFixedSize
		{
			get
			{
				return ((IList)mBusinessObject).IsFixedSize;
			}
		}

		public void Remove(object value)
		{
			this.OnObjectRemoved(new BusinessObjectEventArgs((IBusinessObject)value));
			((IList)mBusinessObject).Remove(value);
		}

		object IList.this[int index]
		{
			get
			{
				return ((IList)mBusinessObject)[index];
			}
			set
			{
				((IList)mBusinessObject)[index] = value;
			}
		}

		#endregion

		#region Group Methods

		public BusinessObjectList<T> Union(BusinessObjectList<T> list)
		{
			var retval = new BusinessObjectList<T>();
			retval.AddRange(this);
			foreach (T o in list)
			{
				if (!retval.Contains(o))
					retval.Add(o);
			}
			return retval;
		}

		public BusinessObjectList<T> Intersect(BusinessObjectList<T> list)
		{
			var retval = new BusinessObjectList<T>();
			foreach (T o in list)
			{
				if (this.Contains(o))
					retval.Add(o);
			}
			return retval;
		}

		public BusinessObjectList<T> Subtract(BusinessObjectList<T> list)
		{
			var retval = new BusinessObjectList<T>();
			retval.AddRange(this);
			foreach (T o in list)
			{
				while (retval.Contains(o))
					retval.Remove(o);
			}
			return retval;
		}

		#endregion

	}

	public delegate void BusinessObjectEventHandler(object sender, BusinessObjectEventArgs e);

}